﻿#if UNITY_NETWORK
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;


namespace Manox.Libs.Networking.Unity
{
    /// <summary>
    /// Unity network node.
    /// </summary>
    public abstract class UnityNetworkNode : NetworkNode
    {
        /// <summary>Port.</summary>
        public static int Port = 1316;
        /// <summary>Max players count.</summary>
        public static int MaxPlayersCount = 4;
        /// <summary>Max message size.</summary>
        public static int MaxMessageSize = 65535;


        /// <summary>Network match.</summary>
        public NetworkMatch UnityNetworkMatch;
        /// <summary>Match info.</summary>
        public MatchInfo MatchInfo;
        /// <summary>Host id.</summary>
        public int HostId = -1;
        /// <summary>Receive buffer.</summary>
        private byte[] ReceiveBuffer;        



        /// <summary>
        /// Awake.
        /// </summary>
        public override void Awake()
        {
            base.Awake();

            this.UnityNetworkMatch = this.gameObject.AddComponent<NetworkMatch>();            
        }

        /// <summary>
        /// STart.
        /// </summary>
        public override void Start()
        {
            base.Start();

            this.ReceiveBuffer = new byte[UnityNetworkNode.MaxMessageSize];            
        }

        /// <summary>
        /// On destroy.
        /// </summary>
        public override void OnDestroy()
        {            
            base.OnDestroy();
        }

        /// <summary>
        /// On application quit.
        /// </summary>
        public virtual void OnApplicationQuit()
        {
            NetworkTransport.Shutdown();
        }

        /// <summary>
        /// Setup host.
        /// </summary>
        /// <param name="isServer"></param>
        protected void SetupHost(bool isServer)
        {
            Debug.Log("Initializing network transport");

            NetworkTransport.Init();

            ConnectionConfig config = new ConnectionConfig();
            config.AddChannel(QosType.Reliable);
            
            HostTopology topology = new HostTopology(config, UnityNetworkNode.MaxPlayersCount);

            if (isServer)
                this.HostId = NetworkTransport.AddHost(topology, UnityNetworkNode.Port);
            else
                this.HostId = NetworkTransport.AddHost(topology);
        }

        /// <summary>
        /// Update.
        /// </summary>
        public override void FixedUpdate()
        {
            base.FixedUpdate();

            if (this.HostId == -1)
                return;

            var networkEvent = NetworkEventType.Nothing;
            int connectionId;
            int channelId;
            int receivedSize;
            byte error;

            // Get events from the relay connection
            networkEvent = NetworkTransport.ReceiveRelayEventFromHost(this.HostId, out error);

            if (networkEvent == NetworkEventType.ConnectEvent)
                Debug.Log("Relay server connected");
            if (networkEvent == NetworkEventType.DisconnectEvent)
                Debug.Log("Relay server disconnected");
            
            networkEvent = NetworkTransport.ReceiveFromHost(this.HostId, out connectionId, out channelId,
                this.ReceiveBuffer, (int)this.ReceiveBuffer.Length, out receivedSize, out error);

            if ((NetworkError)error != NetworkError.Ok)
                Debug.LogError("Error while receiveing network message: " + (NetworkError)error);

            while (networkEvent != NetworkEventType.Nothing)
            {
                switch (networkEvent)
                {
                    case NetworkEventType.ConnectEvent:
                        this.OnConnectReceived(new UnityNetworkConnectionId(connectionId), channelId, this.ReceiveBuffer, receivedSize);
                        break;
                    case NetworkEventType.DisconnectEvent:
                        this.OnDisconnectReceived(new UnityNetworkConnectionId(connectionId), channelId, this.ReceiveBuffer, receivedSize);
                        break;
                    case NetworkEventType.DataEvent:
                        this.OnDataReceived(new UnityNetworkConnectionId(connectionId), channelId, this.ReceiveBuffer, receivedSize);
                        break;                                       
                }

                networkEvent = NetworkTransport.ReceiveFromHost(this.HostId, out connectionId, out channelId,
                    this.ReceiveBuffer, (int)this.ReceiveBuffer.Length, out receivedSize, out error);

                if ((NetworkError)error != NetworkError.Ok)
                    Debug.LogError("Error while receiveing network message: " + (NetworkError)error);
            } 
        }        

        /// <summary>
        /// On connect received.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="channelId"></param>
        /// <param name="buffer"></param>
        /// <param name="receivedSize"></param>
        public virtual void OnConnectReceived(UnityNetworkConnectionId connectionId, int channelId, byte[] buffer, int receivedSize)
        {
            Debug.Log("New connection established : " + connectionId);
        }

        /// <summary>
        /// On disconnect received.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="channelId"></param>
        /// <param name="buffer"></param>
        /// <param name="receivedSize"></param>
        public virtual void OnDisconnectReceived(UnityNetworkConnectionId connectionId, int channelId, byte[] buffer, int receivedSize)
        {
            Debug.Log("Connection lost : " + connectionId);
        }

        /// <summary>
        /// On disconnect received.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="channelId"></param>
        /// <param name="buffer"></param>
        /// <param name="receivedSize"></param>
        public virtual void OnDataReceived(UnityNetworkConnectionId connectionId, int channelId, byte[] buffer, int receivedSize)
        {
            NetworkMessage message = MessagePacker.Unpack(buffer);            

            if (message != null)
                this.SendMessageToListeners(connectionId, buffer[0], message);            
        }             

        /// <summary>
        /// Send data.
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="data"></param>
        /// <param name="dataLength"></param>
        public override void Send(NetworkConnectionId connection, byte[] data, int dataLength)
        {
            byte error;

            NetworkTransport.Send(this.HostId, (connection as UnityNetworkConnectionId).ConnectionId, 0, data, dataLength, out error);
        }        
    }
}
#endif